package com.example.aop;

import com.alibaba.fastjson.JSONObject;
import com.example.dto.RabbitMqMessageDTO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Mq幂等注解切面实现
 *
 * @author Lv
 * @date 2023/4/1
 */
@SuppressWarnings("all")
@Slf4j
@Aspect
@Component
public class MqIdempotentAop {
    /**
     * 定义切入点
     */
    @Pointcut("@annotation(com.example.annotations.MqIdempotent)")
    public void point() {
    }

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * redis存储mq报文唯一值键
     */
    private static String KEY = "mqUk";

    /**
     * 环绕处理切入点方法
     *
     * @param proceedingJoinPoint 切点对象
     */
    @Around("point()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        // 处理数据
        String uniqueKey = dealData(proceedingJoinPoint);
        log.info("报文唯一值 :{}", uniqueKey);

        // 读取redis值
        String value = (String) redisTemplate.opsForValue().get(KEY + uniqueKey);

        if (value != null) {
            // 存在相同的value则不执行注解标注方法
            log.info("存在重复投递，目标方法不执行，重复投递报文唯一值 :{}", uniqueKey);
        } else {
            // 数据写入redis
            redisTemplate.opsForValue().set(KEY + uniqueKey, uniqueKey);
            // 执行注解标注方法
            executeMethod(proceedingJoinPoint);
        }
    }

    /**
     * 处理数据
     *
     * @param proceedingJoinPoint 切点对象
     */
    private String dealData(ProceedingJoinPoint proceedingJoinPoint) {
        // 获取请求参数
        Object[] args = proceedingJoinPoint.getArgs();
        log.info("参数 :{}", args);

        String param = Arrays.toString(args);
        param = param.substring(0, param.length() - 1);
        param = param.substring(1);
        RabbitMqMessageDTO rabbitMqMessageDTO = JSONObject.parseObject(param, RabbitMqMessageDTO.class);
        String uniqueKey = rabbitMqMessageDTO.getUniqueKey();

        if (uniqueKey == null || "".equals(uniqueKey)) {
            throw new RuntimeException("参数异常 mq唯一值不能为空");
        }
        return uniqueKey;
    }

    /**
     * 执行注解标注方法
     *
     * @param proceedingJoinPoint 切点对象
     */
    private void executeMethod(ProceedingJoinPoint proceedingJoinPoint) {
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}
